package server;

import java.net.Socket;
import java.util.ArrayList;

/**
 * This class represents a pool of threads which are created and wait for a request
 * they can process. Once the request is processed, they wait for another one.
 * If there are not enough threads to process a request, more threads will be created
 * If there are too many threads, some will be destroyed
 * @author dzgonjan
 *
 */
public class ThreadBuffer {
	
	private ArrayList<ServerThread> threads;
	private int size = 0;
	private Object threadLock;
	
	/**
	 * Creates a new ThreadBuffer object
	 * @param size - The initial number of threads in the buffer
	 */
	public ThreadBuffer(int size){
		threads = new ArrayList<ServerThread>(size);
		this.threadLock = new Object();
		createAndStartThreads(size);
	}
	
	/**
	 * Creates and starts a specified number of new threads.
	 * @param numNewThreads - The number of threads to create.
	 */
	public void createAndStartThreads(int numNewThreads){
		this.size += numNewThreads;
		for (int i = 0; i<numNewThreads; i++){
			ServerThread serverThread = new ServerThread(this.threadLock);
			this.threads.add(serverThread);
			new Thread(serverThread).start();
		}
	}
	
	/**
	 * Assigns the client request to a free thread.
	 * @param clientSocket
	 */
	public void processRequest(Socket clientSocket){
		boolean full = true;
		
		for (int i = 0; i < this.threads.size(); i++){
			if (!threads.get(i).getBusy()) {
				threads.get(i).setSocket(clientSocket);
				
				//Notify all threads that a new request has arrived
				synchronized(threadLock){threadLock.notifyAll();}
				
				//If there is an empty spot in the first half of the array, try to
				//reduce the size of the array
				if (i<this.size/2) reduceSize();
				
				//Exit the loop
				i = this.threads.size();
				
				//If we found a place then it isn't full
				full = false;
			}
		}
		//If full, create more threads
		if (full) createAndStartThreads(10);
	}
	
	
	/**
	 * Reduces the number of threads if there are too many
	 */
	public void reduceSize(){
		if (this.size<=10) return;
		
		for (int i=0; i<this.size; i++){
			if (!this.threads.get(i).getBusy()) {
				this.threads.get(i).needed = false;
				this.threads.remove(i);
			}
		}
		
		this.threads.trimToSize();
		this.size = this.threads.size();
	}
	
}
